﻿using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MQTTnet;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace MQTTWPFServer
{
    internal class MainWindowViewModel : ObservableObject
    {
        MqttServer mqttServer;

        private string ip = "192.168.10.108";
        public string IP
        {
            get => ip;
            set => SetProperty(ref ip, value);
        }

        private int port = 1883;
        public int Port
        {
            get => port;
            set => SetProperty(ref port, value);
        }

        private string userName = "admin";
        public string UserName
        {
            get => userName;
            set => SetProperty(ref userName, value);
        }

        private string password = "admin";
        public string Password
        {
            get => password;
            set => SetProperty(ref password, value);
        }

        private bool isServerStarted;
        public bool IsServerStarted
        {
            get => isServerStarted;
            set => SetProperty(ref isServerStarted, value);
        }

        public ObservableCollection<Client> Clients { get; } = new ObservableCollection<Client>();
        public ObservableCollection<Message> Messages { get; } = new ObservableCollection<Message>();
        public ObservableCollection<Topic> Topics { get; } = new ObservableCollection<Topic>();

        public ICommand StartServerCommand { get; }
        public ICommand StopServerCommand { get; }

        public MainWindowViewModel()
        {
            this.StartServerCommand = new RelayCommand(StartServer);
            this.StopServerCommand = new RelayCommand(StopServer);
        }

        private async void StartServer()
        {
            var mqttServerFactory = new MqttServerFactory();
            var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().WithDefaultEndpointPort(this.Port).Build();
            this.mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions);
            this.mqttServer.ClientConnectedAsync += Server_ClientConnectedAsync;
            this.mqttServer.ClientDisconnectedAsync += MqttServer_ClientDisconnectedAsync;
            this.mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;
            this.mqttServer.ClientSubscribedTopicAsync += MqttServer_ClientSubscribedTopicAsync;
            this.mqttServer.ClientUnsubscribedTopicAsync += MqttServer_ClientUnsubscribedTopicAsync;
            this.mqttServer.StoppedAsync += MqttServer_StoppedAsync;
            await this.mqttServer.StartAsync();
            this.IsServerStarted = this.mqttServer.IsStarted;
        }

        private async void StopServer()
        {
            if(this.mqttServer != null)
            {
                await this.mqttServer.StopAsync();
                this.IsServerStarted = false;
            }
        }

        private async Task MqttServer_StoppedAsync(EventArgs arg)
        {
            this.Clients.Clear();
            this.Topics.Clear();
            this.Messages.Clear();
        }

        private async Task MqttServer_ClientUnsubscribedTopicAsync(ClientUnsubscribedTopicEventArgs arg)
        {
            Application.Current.Dispatcher.Invoke(() => 
            {
                var topic = this.Topics.SingleOrDefault(t => t.Title == arg.TopicFilter);
                if (topic != null)
                {
                    if (topic.Subscribers.Count > 0)
                    {
                        topic.Subscribers.Remove(arg.ClientId);
                        topic.Subscribes -= 1;
                    }
                }
            });
            
        }

        private async Task MqttServer_ClientSubscribedTopicAsync(ClientSubscribedTopicEventArgs arg)
        {
            Application.Current.Dispatcher.Invoke(() => 
            {
                var topic = this.Topics.SingleOrDefault(t => t.Title == arg.TopicFilter.Topic);
                if (topic == null)
                {
                    topic = new Topic { Title = arg.TopicFilter.Topic, Subscribes = 1 };
                    this.Topics.Add(topic);
                    topic.Subscribers.Clear();
                    topic.Subscribers.Add(arg.ClientId);

                }
                else
                {
                    topic.Subscribes += 1;
                    topic.Subscribers.Add(arg.ClientId);
                }
            });
        }

        private async Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs arg)
        {
            if (arg.ClientId == "server") { return; }
            await Application.Current.Dispatcher.InvokeAsync(async () =>
            {
                var content = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);
                this.Messages.Add(new Message { Topic = arg.ApplicationMessage.Topic, Content = content, ClientId = arg.ClientId });
                //可对特定消息进行处理
                //if(arg.ApplicationMessage.Topic == "Metting"){   }
                //如果主题有其他客户端订阅，系统会自动转发，无需手动编写代码进行转发
            });
        }

        private async System.Threading.Tasks.Task Server_ClientConnectedAsync(ClientConnectedEventArgs arg)
        {
            if (arg.UserName == this.UserName && arg.Password == this.Password)
            {
                if (this.Clients.SingleOrDefault(c => c.ClientId == arg.ClientId) == null)
                {
                    await Application.Current.Dispatcher.InvokeAsync(() => 
                    {
                        this.Clients.Add(new Client { ClientId = arg.ClientId });
                    });
                }
            }
        }

        private async Task MqttServer_ClientDisconnectedAsync(ClientDisconnectedEventArgs arg)
        {
            var client = this.Clients.SingleOrDefault(c => c.ClientId == arg.ClientId);
            if (client != null)
            {
                await Application.Current.Dispatcher.InvokeAsync(() =>
                {
                    this.Clients.Remove(client);
                });
               
            }
        }
    }
}
